using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.IO.Compression;

namespace PsExport
{
    /// <summary>
    /// 一个与具体平台/库解耦的通用导出器：
    /// 通过调用方提供的适配器函数，从一组“焊接操作”对象中提取机器人、位置、关节值等信息，
    /// 生成与RTR相兼容的最小化project.yaml。
    /// </summary>
    public static class WeldOperationRTRExporter
    {
        /// <summary>
        /// 从一组焊接操作导出最小RTR项目（仅生成 project.yaml）。
        /// 通过回调适配器抽取所需数据，以避免对特定CAD/仿真SDK的编译期依赖。
        /// </summary>
        /// <typeparam name="TWeldOperation">焊接操作类型</typeparam>
        /// <typeparam name="TRobot">机器人类型</typeparam>
        /// <typeparam name="TLocation">位置类型</typeparam>
        /// <param name="weldOperations">焊接操作集合</param>
        /// <param name="getRobot">从焊接操作获取机器人</param>
        /// <param name="getRobotName">获取机器人名字</param>
        /// <param name="getLocations">从焊接操作获取其位置序列</param>
        /// <param name="getLocationName">获取位置显示名</param>
        /// <param name="getLocationId">获取位置唯一ID（会用于生成唯一Target名）</param>
        /// <param name="getJointValues">获取位置的关节角序列（按机器人关节顺序）</param>
        /// <param name="getExternalAxesValues">获取位置的外部轴数值序列（可为空）</param>
        /// <param name="outputDirectory">输出根目录（函数会在其中创建项目目录）</param>
        /// <param name="projectName">项目名称（同时作为目录名）</param>
        /// <param name="config">导出配置（连接策略、时间等）</param>
        /// <returns>生成的 project.yaml 的完整路径</returns>
        public static string ExportWeldOperations<TWeldOperation, TRobot, TLocation>(
            IEnumerable<TWeldOperation> weldOperations,
            Func<TWeldOperation, TRobot> getRobot,
            Func<TRobot, string> getRobotName,
            Func<TWeldOperation, IEnumerable<TLocation>> getLocations,
            Func<TLocation, string> getLocationName,
            Func<TLocation, string> getLocationId,
            Func<TLocation, IEnumerable<double>> getJointValues,
            Func<TLocation, IEnumerable<double>> getExternalAxesValues,
            string outputDirectory,
            string projectName,
            WeldOperationExportConfig config = null)
        {
            if (weldOperations == null) throw new ArgumentNullException(nameof(weldOperations));
            if (string.IsNullOrWhiteSpace(outputDirectory)) throw new ArgumentException("outputDirectory 不能为空", nameof(outputDirectory));
            if (string.IsNullOrWhiteSpace(projectName)) throw new ArgumentException("projectName 不能为空", nameof(projectName));

            config ??= new WeldOperationExportConfig();

            // 1) 准备输出目录
            var projectDir = Path.Combine(outputDirectory, projectName);
            PrepareCleanDirectory(projectDir);

            // 2) 按机器人聚合位置
            // RobotName -> List<Location>
            var robotToLocations = new Dictionary<string, List<TLocation>>(StringComparer.OrdinalIgnoreCase);

            foreach (var weldOp in weldOperations)
            {
                var robot = getRobot(weldOp);
                if (robot == null) continue; // 跳过无机器人操作

                var robotName = getRobotName(robot);
                if (string.IsNullOrWhiteSpace(robotName)) continue;

                if (!robotToLocations.TryGetValue(robotName, out var list))
                {
                    list = new List<TLocation>();
                    robotToLocations.Add(robotName, list);
                }

                var locs = getLocations(weldOp) ?? Enumerable.Empty<TLocation>();
                list.AddRange(locs);
            }

            // 3) 生成 YAML（最小结构：project -> workcell.robots + roadmaps）
            var yaml = new StringBuilder();
            yaml.AppendLine("rtr_project:");
            yaml.AppendLine("  rtr_yaml_version: \"4.0.0\"");
            yaml.Append("  project_name: \"").Append(EscapeYaml(projectName)).AppendLine("\"");
            yaml.AppendLine("  workcell:");
            yaml.AppendLine("    robots:");

            foreach (var kv in robotToLocations)
            {
                yaml.AppendLine("    - name: \"" + EscapeYaml(kv.Key) + "\"");
                // 可扩展 joint_limits 等，这里省略，仅保留核心结构
            }

            yaml.AppendLine("  roadmaps:");

            foreach (var kv in robotToLocations)
            {
                var robotName = kv.Key;
                var locations = kv.Value;

                yaml.AppendLine("  - robot: \"" + EscapeYaml(robotName) + "\"");
                yaml.AppendLine("    preset: \"default\"");
                yaml.AppendLine("    validation_flags:");
                yaml.AppendLine("    - all_direct_connections_valid_error");
                yaml.AppendLine("    - all_targets_reachable_error");
                yaml.AppendLine("    - all_planned_connections_valid_error");

                // Assignments
                yaml.AppendLine("    assignments:");
                foreach (var loc in locations)
                {
                    var target = MakeUniqueTarget(getLocationName(loc), getLocationId(loc));
                    var joints = getJointValues(loc) ?? Enumerable.Empty<double>();
                    var exts = getExternalAxesValues?.Invoke(loc) ?? Enumerable.Empty<double>();
                    var jointVector = joints.Concat(exts).ToList();

                    yaml.AppendLine("    - target: \"" + EscapeYaml(target) + "\"");
                    yaml.AppendLine("      joint_config: [" + string.Join(", ", jointVector.Select(FormatDouble)) + "]");
                }

                // Connections
                yaml.AppendLine("    connections:");

                if (locations.Count <= 1)
                {
                    // 无连接可生成，保持空列表项
                    yaml.AppendLine("    - {}");
                }
                else if (config.AutoConnectAll)
                {
                    // 单条 planned ，包含全部顺序 targets
                    yaml.AppendLine("    - planned:");
                    yaml.AppendLine("        targets:");
                    foreach (var loc in locations)
                    {
                        var target = MakeUniqueTarget(getLocationName(loc), getLocationId(loc));
                        yaml.AppendLine("        - \"" + EscapeYaml(target) + "\"");
                    }
                    if (config.DenseConnection)
                    {
                        yaml.AppendLine("        dense: true");
                    }
                    yaml.AppendLine("        planning_time: " + FormatDouble(config.SegmentTime / 1000.0));
                }
                else
                {
                    // 相邻两两 planned 连接
                    for (int i = 0; i < locations.Count - 1; i++)
                    {
                        var a = MakeUniqueTarget(getLocationName(locations[i]), getLocationId(locations[i]));
                        var b = MakeUniqueTarget(getLocationName(locations[i + 1]), getLocationId(locations[i + 1]));
                        yaml.AppendLine("    - planned:");
                        yaml.AppendLine("        targets:");
                        yaml.AppendLine("        - \"" + EscapeYaml(a) + "\"");
                        yaml.AppendLine("        - \"" + EscapeYaml(b) + "\"");
                        yaml.AppendLine("        planning_time: " + FormatDouble(config.SegmentTime / 1000.0));
                    }
                }
            }

            // 4) 写入文件
            var yamlPath = Path.Combine(projectDir, "project.yaml");
            File.WriteAllText(yamlPath, yaml.ToString(), new UTF8Encoding(false));

            return yamlPath;
        }

        /// <summary>
        /// 在生成 project.yaml 的同时，打包生成 zip，并保留原始目录。
        /// </summary>
        public static ExportResult ExportWeldOperationsPackage<TWeldOperation, TRobot, TLocation>(
            IEnumerable<TWeldOperation> weldOperations,
            Func<TWeldOperation, TRobot> getRobot,
            Func<TRobot, string> getRobotName,
            Func<TWeldOperation, IEnumerable<TLocation>> getLocations,
            Func<TLocation, string> getLocationName,
            Func<TLocation, string> getLocationId,
            Func<TLocation, IEnumerable<double>> getJointValues,
            Func<TLocation, IEnumerable<double>> getExternalAxesValues,
            string outputDirectory,
            string projectName,
            WeldOperationExportConfig config = null)
        {
            var yamlPath = ExportWeldOperations(
                weldOperations,
                getRobot,
                getRobotName,
                getLocations,
                getLocationName,
                getLocationId,
                getJointValues,
                getExternalAxesValues,
                outputDirectory,
                projectName,
                config);

            var projectDir = Path.Combine(outputDirectory, projectName);
            var zipPath = Path.Combine(outputDirectory, projectName + ".zip");
            if (File.Exists(zipPath)) File.Delete(zipPath);
            ZipFile.CreateFromDirectory(projectDir, zipPath, CompressionLevel.Optimal, true);

            return new ExportResult
            {
                ProjectDirectory = projectDir,
                YamlPath = yamlPath,
                ZipPath = zipPath
            };
        }

        private static void PrepareCleanDirectory(string dir)
        {
            if (Directory.Exists(dir))
            {
                Directory.Delete(dir, true);
            }
            Directory.CreateDirectory(dir);
        }

        private static string MakeUniqueTarget(string name, string id)
        {
            name = name ?? string.Empty;
            id = id ?? string.Empty;
            var safeId = id.Replace(",", "_");
            return string.IsNullOrWhiteSpace(safeId) ? name : ($"{name}_{safeId}");
        }

        private static string EscapeYaml(string value)
        {
            if (value == null) return string.Empty;
            // 简单转义：替换双引号
            return value.Replace("\"", "\\\"");
        }

        private static string FormatDouble(double d)
        {
            // 使用不随文化变化的小数点格式
            return d.ToString("0.####", System.Globalization.CultureInfo.InvariantCulture);
        }
    }

    public sealed class ExportResult
    {
        public string ProjectDirectory { get; set; }
        public string YamlPath { get; set; }
        public string ZipPath { get; set; }
    }

    public sealed class WeldOperationExportConfig
    {
        /// <summary>
        /// 是否自动将所有位置连成一条 planned 连接
        /// </summary>
        public bool AutoConnectAll { get; set; } = true;

        /// <summary>
        /// 是否密集连接（仅在 AutoConnectAll 为真时写入 dense: true）
        /// </summary>
        public bool DenseConnection { get; set; } = true;

        /// <summary>
        /// 连接的规划时间（毫秒）
        /// </summary>
        public int SegmentTime { get; set; } = 100;
    }
}


